/*-------------------------------------------------------------------------
 *
 * nodeWorktablescan.c
 *      routines to handle WorkTableScan nodes.
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      src/backend/executor/nodeWorktablescan.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeWorktablescan.h"

static TupleTableSlot *WorkTableScanNext(WorkTableScanState *node);

/* ----------------------------------------------------------------
 *        WorkTableScanNext
 *
 *        This is a workhorse for ExecWorkTableScan
 * ----------------------------------------------------------------
 */
static TupleTableSlot *
WorkTableScanNext(WorkTableScanState *node)
{
    TupleTableSlot *slot;
    Tuplestorestate *tuplestorestate;

    /*
     * get information from the estate and scan state
     *
     * Note: we intentionally do not support backward scan.  Although it would
     * take only a couple more lines here, it would force nodeRecursiveunion.c
     * to create the tuplestore with backward scan enabled, which has a
     * performance cost.  In practice backward scan is never useful for a
     * worktable plan node, since it cannot appear high enough in the plan
     * tree of a scrollable cursor to be exposed to a backward-scan
     * requirement.  So it's not worth expending effort to support it.
     *
     * Note: we are also assuming that this node is the only reader of the
     * worktable.  Therefore, we don't need a private read pointer for the
     * tuplestore, nor do we need to tell tuplestore_gettupleslot to copy.
     */
    Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));

    tuplestorestate = node->rustate->working_table;

    /*
     * Get the next tuple from tuplestore. Return NULL if no more tuples.
     */
    slot = node->ss.ss_ScanTupleSlot;
    (void) tuplestore_gettupleslot(tuplestorestate, true, false, slot);
    return slot;
}

/*
 * WorkTableScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
 */
static bool
WorkTableScanRecheck(WorkTableScanState *node, TupleTableSlot *slot)
{
    /* nothing to check */
    return true;
}

/* ----------------------------------------------------------------
 *        ExecWorkTableScan(node)
 *
 *        Scans the worktable sequentially and returns the next qualifying tuple.
 *        We call the ExecScan() routine and pass it the appropriate
 *        access method functions.
 * ----------------------------------------------------------------
 */
static TupleTableSlot *
ExecWorkTableScan(PlanState *pstate)
{
    WorkTableScanState *node = castNode(WorkTableScanState, pstate);

    /*
     * On the first call, find the ancestor RecursiveUnion's state via the
     * Param slot reserved for it.  (We can't do this during node init because
     * there are corner cases where we'll get the init call before the
     * RecursiveUnion does.)
     */
    if (node->rustate == NULL)
    {
        WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan;
        EState       *estate = node->ss.ps.state;
        ParamExecData *param;

        param = &(estate->es_param_exec_vals[plan->wtParam]);
        Assert(param->execPlan == NULL);
        Assert(!param->isnull);
        node->rustate = castNode(RecursiveUnionState, DatumGetPointer(param->value));
        Assert(node->rustate);

        /*
         * The scan tuple type (ie, the rowtype we expect to find in the work
         * table) is the same as the result rowtype of the ancestor
         * RecursiveUnion node.  Note this depends on the assumption that
         * RecursiveUnion doesn't allow projection.
         */
        ExecAssignScanType(&node->ss,
                           ExecGetResultType(&node->rustate->ps));

        /*
         * Now we can initialize the projection info.  This must be completed
         * before we can call ExecScan().
         */
        ExecAssignScanProjectionInfo(&node->ss);
    }

    return ExecScan(&node->ss,
                    (ExecScanAccessMtd) WorkTableScanNext,
                    (ExecScanRecheckMtd) WorkTableScanRecheck);
}


/* ----------------------------------------------------------------
 *        ExecInitWorkTableScan
 * ----------------------------------------------------------------
 */
WorkTableScanState *
ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
{
    WorkTableScanState *scanstate;

    /* check for unsupported flags */
    Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

    /*
     * WorkTableScan should not have any children.
     */
    Assert(outerPlan(node) == NULL);
    Assert(innerPlan(node) == NULL);

    /*
     * create new WorkTableScanState for node
     */
    scanstate = makeNode(WorkTableScanState);
    scanstate->ss.ps.plan = (Plan *) node;
    scanstate->ss.ps.state = estate;
    scanstate->ss.ps.ExecProcNode = ExecWorkTableScan;
    scanstate->rustate = NULL;    /* we'll set this later */

    /*
     * Miscellaneous initialization
     *
     * create expression context for node
     */
    ExecAssignExprContext(estate, &scanstate->ss.ps);

    /*
     * initialize child expressions
     */
    scanstate->ss.ps.qual =
        ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);

    /*
     * tuple table initialization
     */
    ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
    ExecInitScanTupleSlot(estate, &scanstate->ss);

    /*
     * Initialize result tuple type, but not yet projection info.
     */
    ExecAssignResultTypeFromTL(&scanstate->ss.ps);

    return scanstate;
}

/* ----------------------------------------------------------------
 *        ExecEndWorkTableScan
 *
 *        frees any storage allocated through C routines.
 * ----------------------------------------------------------------
 */
void
ExecEndWorkTableScan(WorkTableScanState *node)
{
    /*
     * Free exprcontext
     */
    ExecFreeExprContext(&node->ss.ps);

    /*
     * clean out the tuple table
     */
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
    ExecClearTuple(node->ss.ss_ScanTupleSlot);
}

/* ----------------------------------------------------------------
 *        ExecReScanWorkTableScan
 *
 *        Rescans the relation.
 * ----------------------------------------------------------------
 */
void
ExecReScanWorkTableScan(WorkTableScanState *node)
{
    ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);

    ExecScanReScan(&node->ss);

    /* No need (or way) to rescan if ExecWorkTableScan not called yet */
    if (node->rustate)
        tuplestore_rescan(node->rustate->working_table);
}
